/*
 *
 */
#include "gkt_defines.h"
#include "gkt_board.h"
#include "gkt_debug.h"
#include "gkt_os.h"
#include "gkt_wdt.h"
#include "gkt_wdt_ext.h"

#if defined(GKT_CONFIG_WDTE_ENABLE) && GKT_CONFIG_WDTE_ENABLE

#define WDTE_FEED_INTERVAL_MS	1000
#ifdef GKT_CONFIG_WDTE_WDT_EXPIRE_MS
#define WDTE_WDT_EXPIRE_MS	GKT_CONFIG_WDTE_WDT_EXPIRE_MS
#else
#define WDTE_WDT_EXPIRE_MS	(WDTE_FEED_INTERVAL_MS * 5)
#endif

#undef GKT_WDTE_DESC
#define GKT_WDTE_DESC(name, timeout_ms)	\
	{	\
		.timeout_ticks	= (gkt_aligned(timeout_ms, WDTE_FEED_INTERVAL_MS) / WDTE_FEED_INTERVAL_MS) + 1,	\
	}

typedef struct {
	uint32_t	timeout_ticks;
} gkt_wdte_desc_s;
static const gkt_wdte_desc_s sc_wdte_descs[] = {
	GKT_CONFIG_WDTE_DESC_TABLE
};

extern __RETMEM_USR uint32_t s_wdte_busy_bits;

typedef struct _wdte {
	gkt_mutex_t	mutex;
	uint32_t	current_ticks;
	uint32_t	idle_ticks[GKT_WDTE_ID_MAX];
} wdte_s;
static wdte_s s_wdte;

static void wdte_timer_callback(void *params)
{
	uint32_t i, feed = 0;

	gkt_mutex_lock(s_wdte.mutex, GKT_INFINITE);
	s_wdte.current_ticks++;
	if (!s_wdte_busy_bits)
		feed = 1;
	else {
		for (i = 0; i < GKT_WDTE_ID_MAX; i++) {
			if ((s_wdte_busy_bits & (1U << i))
				&& (s_wdte.current_ticks >= s_wdte.idle_ticks[i])) 
			{
				gkt_info("wdte busy bit:%u timeout - (%u) %u - %u\n", 
						s_wdte_busy_bits, i, s_wdte.current_ticks, s_wdte.idle_ticks[i]);
				break;
			}
		}

		if (i == GKT_WDTE_ID_MAX)
			feed = 1;
	}
	gkt_mutex_unlock(s_wdte.mutex);

	if (feed)
		gkt_wdt_feed();
}

void gkt_wdte_mark(uint32_t id, uint32_t state)
{
	uint32_t mask;

	if (s_wdte.mutex 
		&& (id < GKT_WDTE_ID_MAX)) 
	{
		mask = 1U << id;

		gkt_mutex_lock(s_wdte.mutex, GKT_INFINITE);
		if (GKT_WDTE_ST_IDLE == state)
			s_wdte_busy_bits &= ~mask;
		else {
			s_wdte.idle_ticks[id] = s_wdte.current_ticks + sc_wdte_descs[id].timeout_ticks + 1;
			s_wdte_busy_bits |= mask;
		}
		gkt_mutex_unlock(s_wdte.mutex);
	}
}

/************************************************************
* BPK Key:A total of 32 Uint32_t 
*		  uint32_t [0]:system used
*			   Byte[0]:
*			   		0bit--Whether the mark is hard reset
*			   		1bit--Mark whether the watchdog is reset
*			   		2bit--Mark whether the marking is soft reset
*			   Byte[1]: wdte busy bits
**************************************************************/
int gkt_wdte_init(void)
{
	gkt_soft_timer_t soft_timer;
	int retval;
	uint32_t value;

	if (GKT_SYSTEM_RESET_WTD == g_system_info.reset_mode) {
		G_BPK_ReadKey(&value, 1, GKT_RETMEM_SYSTEM_WAKEUP);
		s_wdte_busy_bits = (value >> 4) & 0xFF;
		gkt_printf("===== wdte busy bits = 0x%x =====\n", s_wdte_busy_bits);
		value = 0x0;
		G_BPK_WriteKey(&value, 1, GKT_RETMEM_SYSTEM_WAKEUP);
	}
	s_wdte_busy_bits = 0;

	s_wdte.mutex = gkt_mutex_create(0);
	if (!s_wdte.mutex) {
		gkt_error("wdte: create mutex failed!\n");
		return GKT_ECREATE;
	}

	soft_timer = gkt_soft_timer_create(GKT_SOFT_TIMER_PERIODIC, 
						wdte_timer_callback, NULL);
	if (!soft_timer) {
		gkt_error("wdte: create soft timer failed!\n");
		return GKT_ECREATE;
	}
	retval = gkt_soft_timer_start(soft_timer, WDTE_FEED_INTERVAL_MS);
	if (retval != GKT_SUCCESS) {
		gkt_error("wdte: start soft timer failed %d\n", retval);
		return retval;
	}
 
	retval = gkt_wdt_config(WDTE_WDT_EXPIRE_MS);
	if (retval != GKT_SUCCESS) {
		gkt_error("wdte: config wdt failed %d\n", retval);
		return retval;
	}

	gkt_wdt_enable();
	gkt_wdt_feed();
	
	return GKT_SUCCESS;
}

#endif

